package com.nitty.cash.loop.weight

import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Paint.Cap
import android.graphics.RectF
import android.graphics.Shader
import android.graphics.SweepGradient
import android.util.AttributeSet
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import com.nitty.cash.loop.R
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.max
import kotlin.math.pow
import kotlin.math.sin
import kotlin.math.sqrt


/**
 * 圆弧滑块View
 */
class ArcSliderKot @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    // 圆弧
    private var mArcPaint = Paint() // 圆弧画笔
    private var mArcPaintWidth = 0f // 圆弧画笔宽度
    private var mArcRadius = 0f     // 圆弧半径
    private var mArcBgColor: Int // 圆弧背景颜色
    private var mArcPgColor: Int // 圆弧进度颜色
    private var mArcShader: Shader? = null // 圆弧着色器
    private var mArcUseShader = false      // 是否使用着色器

    // 滑块
    private var mThumbPaintWidth = 0f // 滑块画笔宽度
    private var mThumbCenterX = 0f // 滑块中心X坐标
    private var mThumbCenterY = 0f // 滑块中心Y坐标
    private var mThumbRadius = 0f  // 滑块半径
    private var mThumbColor: Int // 滑块颜色

    // 角度
    private var mStartAngle = 180 // 开始角度
    private var mSweepAngle = 180 // 扫过角度
    private var calcLastAngle = 0 // 最近的端点角度

    // 位置
    private var mCenterX = 0f // 圆心X坐标
    private var mCenterY = 0f // 圆心Y坐标

    // 进度
    private var mPgMax = 100   // 最大进度
    private var mPgCurr = 50    // 当前进度

    // 拖拽响应
    private var mCanDrag = false // 用户当前是否可拖拽

    // 其他
    private var mAnimDuration = 500 // 动画持续时间
    private var mOnChangeListener: OnChangeListener? = null // 进度触摸事件监听器


    init {
        mArcPaintWidth = 12f//NittyDensityUtil.dp2px(12f).toFloat()
        mArcBgColor = context.getColor(R.color.black)
        mArcPgColor = context.getColor(R.color.theme)

        mThumbRadius = 12f//NittyDensityUtil.dp2px(12f).toFloat()
        mThumbPaintWidth = mThumbRadius
        mThumbColor = context.getColor(R.color.theme)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        var widthMode = MeasureSpec.getMode(widthMeasureSpec)
        var widthSize = MeasureSpec.getSize(widthMeasureSpec)
        var heightMode = MeasureSpec.getMode(heightMeasureSpec)
        var heightSize = MeasureSpec.getSize(heightMeasureSpec)

        if (widthMode == MeasureSpec.EXACTLY) {
            heightMode = MeasureSpec.EXACTLY
            heightSize = widthSize / 2
        } else if (heightMode == MeasureSpec.EXACTLY) {
            widthMode = MeasureSpec.EXACTLY
            widthSize = heightSize * 2
        } else {
            widthMode = MeasureSpec.EXACTLY
            heightMode = MeasureSpec.EXACTLY
            widthSize = heightSize * 2
        }

        // 圆心坐标
        mCenterX = widthSize / 2f // paddingLeft是负数 paddingRight是正数
        mCenterY = heightSize * 1f // paddingTop是负数 paddingBottom是正数
        // 计算padding最大间距
        // 半径 = 视图宽度 - 横向或纵向内间距值 - 画笔宽度
        mArcRadius = ((widthSize - max(mArcPaintWidth.toDouble(), mThumbPaintWidth.toDouble())) / 2f - mThumbRadius).toFloat()

        super.onMeasure(
            MeasureSpec.makeMeasureSpec(widthSize, widthMode),
            MeasureSpec.makeMeasureSpec(heightSize, heightMode)
        )
    }

    override fun onDraw(canvas: Canvas) {
        drawArc(canvas)
        drawThumb(canvas)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN -> checkCanDrag(event.x, event.y)
            MotionEvent.ACTION_MOVE ->
                if (mCanDrag) updateDragThumb(event.x, event.y)

            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                parent.requestDisallowInterceptTouchEvent(false)
                mOnChangeListener?.onStopTrackingTouch(mCanDrag)
                mCanDrag = false
                invalidate()
            }
        }
        return true // 使用 super.onTouchEvent 会导致接收不到 ACTION_MOVE
    }

    // ========== 图形绘制 ==========

    /** 绘制弧形 */
    private fun drawArc(canvas: Canvas) {
        mArcPaint.reset()
        mArcPaint.isAntiAlias = true
        mArcPaint.style = Paint.Style.STROKE
        mArcPaint.strokeWidth = mArcPaintWidth
        mArcPaint.strokeCap = Cap.ROUND

        // 进度圆半径
        val rectF1 = RectF(mCenterX - mArcRadius, mCenterY - mArcRadius, mCenterX + mArcRadius, mCenterY + mArcRadius)

        if (mArcBgColor != 0) {
            mArcPaint.setShader(null)
            mArcPaint.color = mArcBgColor
            // 绘制底层弧形
            canvas.drawArc(rectF1, mStartAngle.toFloat(), mSweepAngle.toFloat(), false, mArcPaint)
        }

        // 着色器不为空则设置着色器，反之用纯色
        if (mArcUseShader && mArcShader != null) {
            mArcPaint.setShader(mArcShader)
        } else {
            mArcPaint.color = mArcPgColor
        }

        val ratio = mPgCurr * 1f / mPgMax
        // 绘制当前进度弧形
        if (ratio != 0f)
            canvas.drawArc(rectF1, mStartAngle.toFloat(), mSweepAngle * ratio, false, mArcPaint)
    }

    /** 绘制滑框 */
    private fun drawThumb(canvas: Canvas) {
        mArcPaint.reset()
        mArcPaint.isAntiAlias = true
        mArcPaint.style = Paint.Style.FILL_AND_STROKE
        mArcPaint.strokeWidth = mThumbPaintWidth
        mArcPaint.color = mThumbColor
        // 滑块所在角度
        val thumbAngle = mStartAngle + mSweepAngle * mPgCurr * 1f / mPgMax
        // 已知圆心，半径，角度，求圆上的点坐标
        mThumbCenterX = (mCenterX + mArcRadius * cos(Math.toRadians(thumbAngle.toDouble()))).toFloat()
        mThumbCenterY = (mCenterY + mArcRadius * sin(Math.toRadians(thumbAngle.toDouble()))).toFloat()
        canvas.drawCircle(mThumbCenterX, mThumbCenterY, mThumbRadius, mArcPaint)
    }

    /**
     * 更新拖拽后View变化
     *
     * @param x 拖拽停止x坐标
     * @param y 拖拽停止y坐标
     */
    private fun updateDragThumb(x: Float, y: Float) {
        val progress = calcProgressByAngle(calcTouchAngle(x, y))
        setCurrPg(progress, true)
    }

    // ========== 状态判断 ==========

    /** 检测坐标点是否可拖拽 */
    private fun checkCanDrag(x: Float, y: Float) {
        val distance = calcPointDistance(mThumbCenterX, mThumbCenterY, x, y)
        mCanDrag = distance <= mThumbRadius
        mOnChangeListener?.onStartTrackingTouch(mCanDrag)
        invalidate()
    }

    // ========== 计算工具 ==========

    /** 获取触摸坐标的夹角度数 */
    private fun calcTouchAngle(x: Float, y: Float): Int {
        val x1 = x - mCenterX
        val y1 = y - mCenterY
        // 求触摸点弧形的夹角度数
        var angle = (atan2(y1.toDouble(), x1.toDouble()) * 180 / Math.PI).toInt()
        // 记录最近的端点角度（用于边角校准）
        if (angle in -10..-1) // 靠近角度0时就记录
            calcLastAngle = 180
        else if (angle in -179..-170) // 靠近角度-180时就记录（因为angle=-180时就自动变成angle=0了）
            calcLastAngle = 0
        // 最终角度
        angle =
            if (angle < 0) angle + 180 // 转为正向旋转度数，用于绘制
            else calcLastAngle // 越界角度均设为端点角度
        return angle
    }

    /** 获取两点间距离 */
    private fun calcPointDistance(x1: Float, y1: Float, x2: Float, y2: Float): Float {
        // pow是x次方，sqrt是平方根
        return sqrt((x1 - x2).pow(2f) + (y1 - y2).pow(2f))
    }

    /** 通过角度换算得到当前进度 */
    private fun calcProgressByAngle(angle: Int): Int {
        // 每角度对应的进度 * 角度 = 进度
        val touchProgress = Math.round(mPgMax * 1f / mSweepAngle * angle)
        return touchProgress
    }

    // ========== 外部设置 ==========

    /**
     * 显示进度动画效果
     *
     * @param fromPg 起始进度（不传则把当前进度作为起始）
     * @param toPg 目标进度
     */
    fun showAnimation(fromPg: Int? = null, toPg: Int) {
        fromPg?.let { mPgCurr = it }
        val valueAnimator = ValueAnimator.ofInt(mPgCurr, toPg)
        valueAnimator.setDuration(mAnimDuration.toLong())
        valueAnimator.addUpdateListener { animation -> setCurrPg(animation.animatedValue as Int) }
        valueAnimator.start()
    }

    /** 设置最大进度 */
    fun setMaxPg(max: Int) {
        if (max > 0) {
            if (mPgCurr > max) mPgCurr = max
            mPgMax = max
            invalidate()
        }
    }

    /**
     * 设置进度
     *
     * @param progress 进度
     * @param fromUser 修改是否来自用户
     */
    private fun setCurrPg(progress: Int, fromUser: Boolean = false) {
        mPgCurr =
            if (progress < 0) 0
            else if (progress > mPgMax) mPgMax
            else progress
        invalidate()
        mOnChangeListener?.onProgressChanged(mPgCurr.toFloat(), mPgMax.toFloat(), fromUser)
    }

    /** 设置弧度背景颜色 */
    fun setArcBgColor(color: Int) {
        mArcBgColor = color
        invalidate()
    }

    /**
     * 设置进度颜色
     *
     * @param colors 长度=1（视为单色）  长度>1（视为渐变色）
     */
    fun setArcPgColor(vararg colors: Int) {
        if (colors.isEmpty()) return
        if (colors.size == 1) {
            mArcPgColor = colors[0]
            mArcUseShader = false
        } else {
            mArcShader = SweepGradient(mCenterX, mCenterX, colors, null)
            mArcUseShader = true
        }
        invalidate()
    }

    /** 获取最大进度 */
    fun getMax(): Int {
        return mPgMax
    }

    /** 获取当前进度 */
    fun getProgress(): Int {
        return mPgCurr
    }

    /** 设置进度改变监听 */
    fun setOnChangeListener(onChangeListener: OnChangeListener?) {
        mOnChangeListener = onChangeListener
    }

    interface OnChangeListener {
        /** 触摸事件开始[MotionEvent.ACTION_DOWN] */
        fun onStartTrackingTouch(isCanDrag: Boolean)

        /** 进度改变时回调此方法 */
        fun onProgressChanged(progress: Float, max: Float, fromUser: Boolean)

        /** 触摸事件停止[MotionEvent.ACTION_UP] */
        fun onStopTrackingTouch(isCanDrag: Boolean)

        /** 点击事件改变进度[GestureDetector] */
        fun onSingleTapUp()
    }
}